﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
using TaskbarHelper;
using TiledShell.Models;
using TiledShell.Views;
using System.ComponentModel;
using System.Windows.Media.Animation;

namespace TiledShell.Windows
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class DesktopWindow : Window
    {
        DispatcherTimer timeDate_Timer = new System.Windows.Threading.DispatcherTimer();
        private ObservableCollection<Tile> Tiles = TileListManager.Tiles;

        public DesktopWindow()
        {
            InitializeComponent();
        }
        
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Hide this window from Alt+Tab.
            //ProtectWindow();

            // Hide the taskbar and Start menu/Windows Aero orb.
            Taskbar.Visible = false;

            // Hide Windows Explorer desktop.
            Native.Functions.DesktopManipulation.ShowDesktopIcons(false);

            // Set up clock timer.
            timeDate_Timer.Tick += new EventHandler(timeDate_Timer_Tick);
            timeDate_Timer.Interval = new TimeSpan(0, 0, 1);
            timeDate_Timer.Start();

            Tiles.CollectionChanged += new NotifyCollectionChangedEventHandler(Tiles_CollectionChanged);
            SetUpTiles();
        }

        private void SetUpTiles()
        {
            TileListManager.LoadTiles();
            if (Tiles.Count == 0)
            {
                var tile1 = new Tile("c:\\windows\\explorer.exe");
                var tile2 = new Tile("c:\\windows\\system32\\mspaint.exe");
                var tile3 = new Tile("AddNew");
                Tiles.Add(tile1);
                Tiles.Add(tile2);
                Tiles.Add(tile3);
            }
        }

        void Tiles_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Replace)
            {
                int index = e.OldStartingIndex;
                foreach (var item in e.OldItems)
                {
                    ((Tile)item).PropertyChanged -= new PropertyChangedEventHandler(Tile_PropertyChanged);
                    DesktopPanel.Children.RemoveAt(index);
                    index++;
                }
            }

            if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Replace)
            {
                int index = e.NewStartingIndex;
                foreach (var item in e.NewItems)
                {
                    ((Tile)item).PropertyChanged += new PropertyChangedEventHandler(Tile_PropertyChanged);
                    DesktopPanel.Children.Insert(index, new TileView((Tile)item));
                    index++;
                }
            }
            
            if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                DesktopPanel.Children.Clear();
                for (int i = 0; i < Tiles.Count; i++)
                {
                    DesktopPanel.Children.Add(new TileView(Tiles[i]));
                }
            }

            if (e.Action == NotifyCollectionChangedAction.Move)
            {
                //Figure out logic for this.
            }

            ResizeTileViews();
            TileListManager.WriteToDisk();
        }

        void Tile_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "NumClicks")
            {
                ResizeTileViews();
                TileListManager.WriteToDisk();
            }

        }

        private void ResizeTileViews()
        {
            double width = DesktopPanel.ActualWidth;
            double height = DesktopPanel.ActualHeight;

            int totalClicks = 0;
            foreach (var item in Tiles)
            {
                totalClicks += item.NumClicks;
            }
  
            int index = 0;
            foreach (var item in Tiles)
            {
                double percentArea = .05;
                if (totalClicks != 0)
                {
                    percentArea = (double)item.NumClicks / (double)totalClicks;
                }
                var localArea = percentArea * width * height * .75;
                var length = Math.Sqrt(localArea);

                if (length > height / 2)
                    length = height / 2;

                if (length < height / 10)
                    length = height / 10;

                var view = (TileView)DesktopPanel.Children[index];

                var oldwidth = view.Width;

                if (double.IsNaN(oldwidth))
                    oldwidth = view.ActualWidth;

                var widthAnimation = new DoubleAnimation();
                widthAnimation.To = length;
                widthAnimation.From = oldwidth;
                widthAnimation.Duration = TimeSpan.FromSeconds(1);

                view.BeginAnimation(FrameworkElement.WidthProperty, widthAnimation);
                view.BeginAnimation(FrameworkElement.HeightProperty, widthAnimation);
                view.Width = length;
                view.Height = length;

                index++;
            }
        }

        // When the window is closing...
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            timeDate_Timer.Stop();
            Taskbar.Visible = true;
            Native.Functions.DesktopManipulation.ShowDesktopIcons(true);
        }

        private void PowerOff_Click(object sender, RoutedEventArgs e)
        {
            var win = new ShutDownWindow();
            win.ShowDialog();
        }

        private void timeDate_Timer_Tick(object sender, EventArgs e)
        {
            TimeBox.Text = DateTime.Now.ToShortTimeString();
        }

        #region Stuff to hide window from alt-tab (make it desktop like)
        private void ProtectWindow()
        {
            WindowInteropHelper wndHelper = new WindowInteropHelper(this);
            int exStyle = (int)GetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE);
            exStyle |= (int)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
            SetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
        }

        [Flags]
        public enum ExtendedWindowStyles
        {
            // ...
            WS_EX_TOOLWINDOW = 0x00000080,
            // ...
        }

        public enum GetWindowLongFields
        {
            // ...
            GWL_EXSTYLE = (-20),
            // ...
        }

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);

        public static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
        {
            int error = 0;
            IntPtr result = IntPtr.Zero;
            // Win32 SetWindowLong doesn't clear error on success
            SetLastError(0);

            if (IntPtr.Size == 4)
            {
                // use SetWindowLong
                Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
                error = Marshal.GetLastWin32Error();
                result = new IntPtr(tempResult);
            }
            else
            {
                // use SetWindowLongPtr
                result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
                error = Marshal.GetLastWin32Error();
            }

            if ((result == IntPtr.Zero) && (error != 0))
            {
                throw new System.ComponentModel.Win32Exception(error);
            }

            return result;
        }

        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
        private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

        [DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
        private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);

        private static int IntPtrToInt32(IntPtr intPtr)
        {
            return unchecked((int)intPtr.ToInt64());
        }

        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(int dwErrorCode);

        #endregion

    }
}
